﻿using System.Collections.Generic;
using System.Threading;

namespace CoreLibrary.Collection {

    public class CopyOnWriteList<T> : IList<T> {

        private List<T> _list = new List<T>();

        public T this[int index] {
            get {
                return _list[index];
            }
            set {
                List<T> oldList, replacedList;
                do {
                    oldList = _list;
                    var newList = GetNewListForWrite();
                    newList[index] = value;
                    replacedList = Interlocked.CompareExchange(ref _list, newList, oldList);
                }
                while (replacedList != oldList);
            }
        }

        public void Add(T item) {
            List<T> oldList, replacedList;
            do {
                oldList = _list;
                var newList = GetNewListForWrite();
                newList.Add(item);
                replacedList = Interlocked.CompareExchange(ref _list, newList, oldList);
            }
            while (replacedList != oldList);
        }

        public void Clear() {
            _list = new List<T>();
        }

        public bool Contains(T item) {
            return _list.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex) {
            _list.CopyTo(array, arrayIndex);
        }

        public int Count {
            get { return _list.Count; }
        }

        /// <summary>
        /// Gets a copy of the contents of the list. The copy will not change even if the original
        /// CopyOnWriteList object is modified. This method should be used to iterate the list in
        /// a thread-safe way if no lock is taken. Iterating on the original list is not guaranteed 
        /// to be thread-safe.
        /// </summary>
        /// <returns>The returned copy should not be modified by the caller.</returns>
        public List<T> GetCopyForRead() {
            return _list;
        }

        public int IndexOf(T item) {
            return _list.IndexOf(item);
        }

        public bool IsReadOnly {
            get { return false; }
        }

        public void Insert(int index, T item) {
            List<T> oldList, replacedList;
            do {
                oldList = _list;
                var newList = GetNewListForWrite();
                newList.Insert(index, item);
                replacedList = Interlocked.CompareExchange(ref _list, newList, oldList);
            }
            while (replacedList != oldList);
        }

        public void RemoveAt(int index) {
            List<T> oldList, replacedList;
            do {
                oldList = _list;
                var newList = GetNewListForWrite();
                newList.RemoveAt(index);
                replacedList = Interlocked.CompareExchange(ref _list, newList, oldList);
            }
            while (replacedList != oldList);
        }

        public bool Remove(T item) {
            List<T> oldList, replacedList;
            bool ret;
            do {
                oldList = _list;
                var newList = GetNewListForWrite();
                ret = newList.Remove(item);
                replacedList = Interlocked.CompareExchange(ref _list, newList, oldList);
            }
            while (replacedList != oldList);
            return ret;
        }

        public IEnumerator<T> GetEnumerator() {
            return _list.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            return ((System.Collections.IEnumerable)_list).GetEnumerator();
        }

        private List<T> GetNewListForWrite() {
            var oldList = _list;
            var newList = new List<T>(oldList.Count + 1);
            newList.AddRange(oldList);
            return newList;
        }

    }

}